// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/* Generated from 'MathContext.nrx' 8 Sep 2000 11:07:48 [v2.00] */
/* Options: Binary Comments Crossref Format Java Logo Strictargs Strictcase Trace2 Verbose3 */
package com.ibm.icu.math;

/* ------------------------------------------------------------------ */
/* MathContext -- Math context settings                               */
/* ------------------------------------------------------------------ */
/* Copyright IBM Corporation, 1997, 2000, 2005, 2007.  All Rights Reserved. */
/*                                                                    */
/*   The MathContext object encapsulates the settings used by the     */
/*   BigDecimal class; it could also be used by other arithmetics.    */
/* ------------------------------------------------------------------ */
/* Notes:                                                             */
/*                                                                    */
/* 1. The properties are checked for validity on construction, so     */
/*    the BigDecimal class may assume that they are correct.          */
/* ------------------------------------------------------------------ */
/* Author:    Mike Cowlishaw                                          */
/* 1997.09.03 Initial version (edited from netrexx.lang.RexxSet)      */
/* 1997.09.12 Add lostDigits property                                 */
/* 1998.05.02 Make the class immutable and final; drop set methods    */
/* 1998.06.05 Add Round (rounding modes) property                     */
/* 1998.06.25 Rename from DecimalContext; allow digits=0              */
/* 1998.10.12 change to com.ibm.icu.math package                          */
/* 1999.02.06 add javadoc comments                                    */
/* 1999.03.05 simplify; changes from discussion with J. Bloch         */
/* 1999.03.13 1.00 release to IBM Centre for Java Technology          */
/* 1999.07.10 1.04 flag serialization unused                          */
/* 2000.01.01 1.06 copyright update                                   */
/* ------------------------------------------------------------------ */

/**
 * The <code>MathContext</code> immutable class encapsulates the settings understood by the operator
 * methods of the {@link BigDecimal} class (and potentially other classes). Operator methods are
 * those that effect an operation on a number or a pair of numbers.
 *
 * <p>The settings, which are not base-dependent, comprise:
 *
 * <ol>
 *   <li><code>digits</code>: the number of digits (precision) to be used for an operation
 *   <li><code>form</code>: the form of any exponent that results from the operation
 *   <li><code>lostDigits</code>: whether checking for lost digits is enabled
 *   <li><code>roundingMode</code>: the algorithm to be used for rounding.
 * </ol>
 *
 * <p>When provided, a <code>MathContext</code> object supplies the settings for an operation
 * directly.
 *
 * <p>When <code>MathContext.DEFAULT</code> is provided for a <code>MathContext</code> parameter
 * then the default settings are used (<code>9, SCIENTIFIC, false, ROUND_HALF_UP</code>).
 *
 * <p>In the <code>BigDecimal</code> class, all methods which accept a <code>MathContext</code>
 * object defaults) also have a version of the method which does not accept a MathContext parameter.
 * These versions carry out unlimited precision fixed point arithmetic (as though the settings were
 * (<code>0, PLAIN, false, ROUND_HALF_UP</code>).
 *
 * <p>The instance variables are shared with default access (so they are directly accessible to the
 * <code>BigDecimal</code> class), but must never be changed.
 *
 * <p>The rounding mode constants have the same names and values as the constants of the same name
 * in <code>java.math.BigDecimal</code>, to maintain compatibility with earlier versions of <code>
 * BigDecimal</code>.
 *
 * @see BigDecimal
 * @author Mike Cowlishaw
 * @stable ICU 2.0
 */
public final class MathContext implements java.io.Serializable {
    // private static final java.lang.String $0="MathContext.nrx";

    /* ----- Properties ----- */
    /* properties public constant */
    /**
     * Plain (fixed point) notation, without any exponent. Used as a setting to control the form of
     * the result of a <code>BigDecimal</code> operation. A zero result in plain form may have a
     * decimal part of one or more zeros.
     *
     * @see #ENGINEERING
     * @see #SCIENTIFIC
     * @stable ICU 2.0
     */
    public static final int PLAIN = 0; // [no exponent]

    /**
     * Standard floating point notation (with scientific exponential format, where there is one
     * digit before any decimal point). Used as a setting to control the form of the result of a
     * <code>BigDecimal</code> operation. A zero result in plain form may have a decimal part of one
     * or more zeros.
     *
     * @see #ENGINEERING
     * @see #PLAIN
     * @stable ICU 2.0
     */
    public static final int SCIENTIFIC = 1; // 1 digit before .

    /**
     * Standard floating point notation (with engineering exponential format, where the power of ten
     * is a multiple of 3). Used as a setting to control the form of the result of a <code>
     * BigDecimal</code> operation. A zero result in plain form may have a decimal part of one or
     * more zeros.
     *
     * @see #PLAIN
     * @see #SCIENTIFIC
     * @stable ICU 2.0
     */
    public static final int ENGINEERING = 2; // 1-3 digits before .

    // The rounding modes match the original BigDecimal class values
    /**
     * Rounding mode to round to a more positive number. Used as a setting to control the rounding
     * mode used during a <code>BigDecimal</code> operation.
     *
     * <p>If any of the discarded digits are non-zero then the result should be rounded towards the
     * next more positive digit.
     *
     * @stable ICU 2.0
     */
    public static final int ROUND_CEILING = 2;

    /**
     * Rounding mode to round towards zero. Used as a setting to control the rounding mode used
     * during a <code>BigDecimal</code> operation.
     *
     * <p>All discarded digits are ignored (truncated). The result is neither incremented nor
     * decremented.
     *
     * @stable ICU 2.0
     */
    public static final int ROUND_DOWN = 1;

    /**
     * Rounding mode to round to a more negative number. Used as a setting to control the rounding
     * mode used during a <code>BigDecimal</code> operation.
     *
     * <p>If any of the discarded digits are non-zero then the result should be rounded towards the
     * next more negative digit.
     *
     * @stable ICU 2.0
     */
    public static final int ROUND_FLOOR = 3;

    /**
     * Rounding mode to round to nearest neighbor, where an equidistant value is rounded down. Used
     * as a setting to control the rounding mode used during a <code>BigDecimal</code> operation.
     *
     * <p>If the discarded digits represent greater than half (0.5 times) the value of a one in the
     * next position then the result should be rounded up (away from zero). Otherwise the discarded
     * digits are ignored.
     *
     * @stable ICU 2.0
     */
    public static final int ROUND_HALF_DOWN = 5;

    /**
     * Rounding mode to round to nearest neighbor, where an equidistant value is rounded to the
     * nearest even neighbor. Used as a setting to control the rounding mode used during a <code>
     * BigDecimal</code> operation.
     *
     * <p>If the discarded digits represent greater than half (0.5 times) the value of a one in the
     * next position then the result should be rounded up (away from zero). If they represent less
     * than half, then the result should be rounded down.
     *
     * <p>Otherwise (they represent exactly half) the result is rounded down if its rightmost digit
     * is even, or rounded up if its rightmost digit is odd (to make an even digit).
     *
     * @stable ICU 2.0
     */
    public static final int ROUND_HALF_EVEN = 6;

    /**
     * Rounding mode to round to nearest neighbor, where an equidistant value is rounded up. Used as
     * a setting to control the rounding mode used during a <code>BigDecimal</code> operation.
     *
     * <p>If the discarded digits represent greater than or equal to half (0.5 times) the value of a
     * one in the next position then the result should be rounded up (away from zero). Otherwise the
     * discarded digits are ignored.
     *
     * @stable ICU 2.0
     */
    public static final int ROUND_HALF_UP = 4;

    /**
     * Rounding mode to assert that no rounding is necessary. Used as a setting to control the
     * rounding mode used during a <code>BigDecimal</code> operation.
     *
     * <p>Rounding (potential loss of information) is not permitted. If any of the discarded digits
     * are non-zero then an <code>ArithmeticException</code> should be thrown.
     *
     * @stable ICU 2.0
     */
    public static final int ROUND_UNNECESSARY = 7;

    /**
     * Rounding mode to round away from zero. Used as a setting to control the rounding mode used
     * during a <code>BigDecimal</code> operation.
     *
     * <p>If any of the discarded digits are non-zero then the result will be rounded up (away from
     * zero).
     *
     * @stable ICU 2.0
     */
    public static final int ROUND_UP = 0;

    /* properties shared */
    /**
     * The number of digits (precision) to be used for an operation. A value of 0 indicates that
     * unlimited precision (as many digits as are required) will be used.
     *
     * <p>The {@link BigDecimal} operator methods use this value to determine the precision of
     * results. Note that leading zeros (in the integer part of a number) are never significant.
     *
     * <p><code>digits</code> will always be non-negative.
     *
     * @serial
     */
    int digits;

    /**
     * The form of results from an operation.
     *
     * <p>The {@link BigDecimal} operator methods use this value to determine the form of results,
     * in particular whether and how exponential notation should be used.
     *
     * @see #ENGINEERING
     * @see #PLAIN
     * @see #SCIENTIFIC
     * @serial
     */
    int form; // values for this must fit in a byte

    /**
     * Controls whether lost digits checking is enabled for an operation. Set to <code>true</code>
     * to enable checking, or to <code>false</code> to disable checking.
     *
     * <p>When enabled, the {@link BigDecimal} operator methods check the precision of their operand
     * or operands, and throw an <code>ArithmeticException</code> if an operand is more precise than
     * the digits setting (that is, digits would be lost). When disabled, operands are rounded to
     * the specified digits.
     *
     * @serial
     */
    boolean lostDigits;

    /**
     * The rounding algorithm to be used for an operation.
     *
     * <p>The {@link BigDecimal} operator methods use this value to determine the algorithm to be
     * used when non-zero digits have to be discarded in order to reduce the precision of a result.
     * The value must be one of the public constants whose name starts with <code>ROUND_</code>.
     *
     * @see #ROUND_CEILING
     * @see #ROUND_DOWN
     * @see #ROUND_FLOOR
     * @see #ROUND_HALF_DOWN
     * @see #ROUND_HALF_EVEN
     * @see #ROUND_HALF_UP
     * @see #ROUND_UNNECESSARY
     * @see #ROUND_UP
     * @serial
     */
    int roundingMode;

    /* properties private constant */
    // default settings
    private static final int DEFAULT_FORM = SCIENTIFIC;
    private static final int DEFAULT_DIGITS = 9;
    private static final boolean DEFAULT_LOSTDIGITS = false;
    private static final int DEFAULT_ROUNDINGMODE = ROUND_HALF_UP;

    /* properties private constant */

    private static final int MIN_DIGITS = 0; // smallest value for DIGITS.
    private static final int MAX_DIGITS = 999999999; // largest value for DIGITS.  If increased,
    // the BigDecimal class may need update.
    // list of valid rounding mode values, most common two first
    private static final int ROUNDS[] =
            new int[] {
                ROUND_HALF_UP,
                ROUND_UNNECESSARY,
                ROUND_CEILING,
                ROUND_DOWN,
                ROUND_FLOOR,
                ROUND_HALF_DOWN,
                ROUND_HALF_EVEN,
                ROUND_UP
            };

    private static final java.lang.String ROUNDWORDS[] =
            new java.lang.String[] {
                "ROUND_HALF_UP",
                "ROUND_UNNECESSARY",
                "ROUND_CEILING",
                "ROUND_DOWN",
                "ROUND_FLOOR",
                "ROUND_HALF_DOWN",
                "ROUND_HALF_EVEN",
                "ROUND_UP"
            }; // matching names of the ROUNDS values

    /* properties private constant unused */

    // Serialization version
    private static final long serialVersionUID = 7163376998892515376L;

    /* properties public constant */
    /**
     * A <code>MathContext</code> object initialized to the default settings for general-purpose
     * arithmetic. That is, <code>digits=9 form=SCIENTIFIC lostDigits=false
     * roundingMode=ROUND_HALF_UP</code>.
     *
     * @see #SCIENTIFIC
     * @see #ROUND_HALF_UP
     * @stable ICU 2.0
     */
    public static final com.ibm.icu.math.MathContext DEFAULT =
            new com.ibm.icu.math.MathContext(
                    DEFAULT_DIGITS, DEFAULT_FORM, DEFAULT_LOSTDIGITS, DEFAULT_ROUNDINGMODE);

    /* ----- Constructors ----- */

    /**
     * Constructs a new <code>MathContext</code> with a specified precision. The other settings are
     * set to the default values (see {@link #DEFAULT}).
     *
     * <p>An <code>IllegalArgumentException</code> is thrown if the <code>setdigits</code> parameter
     * is out of range (&lt;0 or &gt;999999999).
     *
     * @param setdigits The <code>int</code> digits setting for this <code>MathContext</code>.
     * @throws IllegalArgumentException parameter out of range.
     * @stable ICU 2.0
     */
    public MathContext(int setdigits) {
        this(setdigits, DEFAULT_FORM, DEFAULT_LOSTDIGITS, DEFAULT_ROUNDINGMODE);
        return;
    }

    /**
     * Constructs a new <code>MathContext</code> with a specified precision and form. The other
     * settings are set to the default values (see {@link #DEFAULT}).
     *
     * <p>An <code>IllegalArgumentException</code> is thrown if the <code>setdigits</code> parameter
     * is out of range (&lt;0 or &gt;999999999), or if the value given for the <code>setform</code>
     * parameter is not one of the appropriate constants.
     *
     * @param setdigits The <code>int</code> digits setting for this <code>MathContext</code>.
     * @param setform The <code>int</code> form setting for this <code>MathContext</code>.
     * @throws IllegalArgumentException parameter out of range.
     * @stable ICU 2.0
     */
    public MathContext(int setdigits, int setform) {
        this(setdigits, setform, DEFAULT_LOSTDIGITS, DEFAULT_ROUNDINGMODE);
        return;
    }

    /**
     * Constructs a new <code>MathContext</code> with a specified precision, form, and lostDigits
     * setting. The roundingMode setting is set to its default value (see {@link #DEFAULT}).
     *
     * <p>An <code>IllegalArgumentException</code> is thrown if the <code>setdigits</code> parameter
     * is out of range (&lt;0 or &gt;999999999), or if the value given for the <code>setform</code>
     * parameter is not one of the appropriate constants.
     *
     * @param setdigits The <code>int</code> digits setting for this <code>MathContext</code>.
     * @param setform The <code>int</code> form setting for this <code>MathContext</code>.
     * @param setlostdigits The <code>boolean</code> lostDigits setting for this <code>MathContext
     *     </code>.
     * @throws IllegalArgumentException parameter out of range.
     * @stable ICU 2.0
     */
    public MathContext(int setdigits, int setform, boolean setlostdigits) {
        this(setdigits, setform, setlostdigits, DEFAULT_ROUNDINGMODE);
        return;
    }

    /**
     * Constructs a new <code>MathContext</code> with a specified precision, form, lostDigits, and
     * roundingMode setting.
     *
     * <p>An <code>IllegalArgumentException</code> is thrown if the <code>setdigits</code> parameter
     * is out of range (&lt;0 or &gt;999999999), or if the value given for the <code>setform</code>
     * or <code>setroundingmode</code> parameters is not one of the appropriate constants.
     *
     * @param setdigits The <code>int</code> digits setting for this <code>MathContext</code>.
     * @param setform The <code>int</code> form setting for this <code>MathContext</code>.
     * @param setlostdigits The <code>boolean</code> lostDigits setting for this <code>MathContext
     *     </code>.
     * @param setroundingmode The <code>int</code> roundingMode setting for this <code>MathContext
     *     </code>.
     * @throws IllegalArgumentException parameter out of range.
     * @stable ICU 2.0
     */
    public MathContext(int setdigits, int setform, boolean setlostdigits, int setroundingmode) {
        super();

        // set values, after checking
        if (setdigits != DEFAULT_DIGITS) {
            if (setdigits < MIN_DIGITS)
                throw new java.lang.IllegalArgumentException("Digits too small:" + " " + setdigits);
            if (setdigits > MAX_DIGITS)
                throw new java.lang.IllegalArgumentException("Digits too large:" + " " + setdigits);
        }
        {
            /*select*/
            if (setform == SCIENTIFIC) {
                // [most common]
            } else if (setform == ENGINEERING) {
            } else if (setform == PLAIN) {
            } else {
                throw new java.lang.IllegalArgumentException("Bad form value:" + " " + setform);
            }
        }
        if ((!(isValidRound(setroundingmode))))
            throw new java.lang.IllegalArgumentException(
                    "Bad roundingMode value:" + " " + setroundingmode);
        digits = setdigits;
        form = setform;
        lostDigits = setlostdigits; // [no bad value possible]
        roundingMode = setroundingmode;
        return;
    }

    /**
     * Returns the digits setting. This value is always non-negative.
     *
     * @return an <code>int</code> which is the value of the digits setting
     * @stable ICU 2.0
     */
    public int getDigits() {
        return digits;
    }

    /**
     * Returns the form setting. This will be one of {@link #ENGINEERING}, {@link #PLAIN}, or {@link
     * #SCIENTIFIC}.
     *
     * @return an <code>int</code> which is the value of the form setting
     * @stable ICU 2.0
     */
    public int getForm() {
        return form;
    }

    /**
     * Returns the lostDigits setting. This will be either <code>true</code> (enabled) or <code>
     * false</code> (disabled).
     *
     * @return a <code>boolean</code> which is the value of the lostDigits setting
     * @stable ICU 2.0
     */
    public boolean getLostDigits() {
        return lostDigits;
    }

    /**
     * Returns the roundingMode setting. This will be one of {@link #ROUND_CEILING}, {@link
     * #ROUND_DOWN}, {@link #ROUND_FLOOR}, {@link #ROUND_HALF_DOWN}, {@link #ROUND_HALF_EVEN},
     * {@link #ROUND_HALF_UP}, {@link #ROUND_UNNECESSARY}, or {@link #ROUND_UP}.
     *
     * @return an <code>int</code> which is the value of the roundingMode setting
     * @stable ICU 2.0
     */
    public int getRoundingMode() {
        return roundingMode;
    }

    /**
     * Returns the <code>MathContext</code> as a readable string. The <code>String</code> returned
     * represents the settings of the <code>MathContext</code> object as four blank-delimited words
     * separated by a single blank and with no leading or trailing blanks, as follows:
     *
     * <ol>
     *   <li><code>digits=</code>, immediately followed by the value of the digits setting as a
     *       numeric word.
     *   <li><code>form=</code>, immediately followed by the value of the form setting as an
     *       uppercase word (one of <code>SCIENTIFIC</code>, <code>PLAIN</code>, or <code>
     *       ENGINEERING</code>).
     *   <li><code>lostDigits=</code>, immediately followed by the value of the lostDigits setting (
     *       <code>1</code> if enabled, <code>0</code> if disabled).
     *   <li><code>roundingMode=</code>, immediately followed by the value of the roundingMode
     *       setting as a word. This word will be the same as the name of the corresponding public
     *       constant.
     * </ol>
     *
     * <p>For example: <br>
     * <code>
     * digits=9 form=SCIENTIFIC lostDigits=0 roundingMode=ROUND_HALF_UP
     * </code>
     *
     * <p>Additional words may be appended to the result of <code>toString</code> in the future if
     * more properties are added to the class.
     *
     * @return a <code>String</code> representing the context settings.
     * @stable ICU 2.0
     */
    @Override
    public java.lang.String toString() {
        java.lang.String formstr = null;
        int r = 0;
        java.lang.String roundword = null;
        {
            /*select*/
            if (form == SCIENTIFIC) formstr = "SCIENTIFIC";
            else if (form == ENGINEERING) formstr = "ENGINEERING";
            else {
                formstr = "PLAIN"; /* form=PLAIN */
            }
        }
        {
            int $1 = ROUNDS.length;
            r = 0;
            r:
            for (; $1 > 0; $1--, r++) {
                if (roundingMode == ROUNDS[r]) {
                    roundword = ROUNDWORDS[r];
                    break r;
                }
            }
        } /*r*/
        return "digits="
                + digits
                + " "
                + "form="
                + formstr
                + " "
                + "lostDigits="
                + (lostDigits ? "1" : "0")
                + " "
                + "roundingMode="
                + roundword;
    }

    /* <sgml> Test whether round is valid. </sgml> */
    // This could be made shared for use by BigDecimal for setScale.

    private static boolean isValidRound(int testround) {
        int r = 0;
        {
            int $2 = ROUNDS.length;
            for (r = 0; $2 > 0; $2--, r++) {
                if (testround == ROUNDS[r]) return true;
            }
        } /*r*/
        return false;
    }
}
